﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Commently;


namespace RulesCore
{
    
    public static class RUCore
    {
        public enum Side
        {
            Left,
            Right
        };

        public struct Limitter
        {
            public int Then;
            public int End;
        }

        public class TreeNode
        {
            private string m_value;
            TreeNode m_right;
            TreeNode m_left;

            public TreeNode Left
            {
                get
                {
                    return this.m_left;
                }
                set
                {
                    this.m_left = value;
                }
            }

            public TreeNode Right
            {
                get
                {
                    return this.m_right;
                }
                set
                {
                    this.m_right = value;
                }
            }
            public string Value
            {
                get
                {
                    return m_value;
                }
                set
                {
                    this.m_value = value;
                }
            }



            public TreeNode(string value, TreeNode right, TreeNode left)
            {
                //TODO: Fill
            }

            public void SetChild(Side side, TreeNode NodeToSet)
            {
                switch (side)
                {
                    case Side.Left:
                        this.Left = NodeToSet;
                        break;
                    case Side.Right:
                        this.Right = NodeToSet;
                        break;
                    default:
                        break;
                }
            }

            public TreeNode()
                : this("", null, null)
            {

            }
        }


        static Limitter[] ThenEndInd;
        private static AndOrRule AOR;
        private static IfThenRule ITR;
        private static NotRule NR;


        public static BaseRule BuildRule(string[] Formula)
        {
            Analyze(Formula);
            //TreeNode RuleTree = new TreeNode();
            TreeNode root = null;
            BuildTree(Formula, 0, Formula.Length - 1, root, Side.Left);

            return CreateRuleFromTree(root);
        }


        public static BaseRule CreateRuleFromTree(TreeNode node)
        {
            BaseRule rule;
            switch (node.Value)
            {
                case "AND": rule = new AndOrRule(CreateRuleFromTree(node.Left), Op.AND, CreateRuleFromTree(node.Right));
                    break;
                case "OR": rule = new AndOrRule(CreateRuleFromTree(node.Left), Op.OR, CreateRuleFromTree(node.Right));
                    break;
                case "IF": rule = new IfThenRule(CreateRuleFromTree(node.Left), CreateRuleFromTree(node.Right));
                    break;
                case "NOT": rule = new NotRule(CreateRuleFromTree(node.Left));
                    break;
                default: rule = new CompareRule(node.Value);
                    break;
            }

            return rule;
        }


        private static void BuildTree(string[] Formula, int start, int end, TreeNode parent, Side side)
        {
            int j = start;
            TreeNode node = null;
            while (Formula[j] != Com.AND && Formula[j] != Com.OR && Formula[j] != Com.IF && Formula[j] != Com.NOT && Formula[j] != Com.THEN && j < end)
            {
                j++;
            }

            if (Formula[j] == Com.AND)
            {
                node = new TreeNode(Com.AND, null, null);
                BuildTree(Formula, start, j - 1, node, Side.Right);
                BuildTree(Formula, j + 1, end, node, Side.Left);
            }
            else if (Formula[j] == Com.OR)
            {
                node = new TreeNode(Com.AND, null, null);
                BuildTree(Formula, start, j - 1, node, Side.Right);
                BuildTree(Formula, j + 1, end, node, Side.Left);
            }
            else if (Formula[j] == Com.NOT)
            {
                node = new TreeNode(Com.NOT, null, null);
                BuildTree(Formula, j + 1, ThenEndInd[j].End, node, Side.Left);
            }
            else if (Formula[j] == Com.IF)
            {
                // Create a corresponding IF node.
                TreeNode ifnode = new TreeNode("IF", null, null);
                BuildTree(Formula, j + 1, ThenEndInd[j].Then - 1, ifnode, Side.Left);
                BuildTree(Formula, ThenEndInd[j].Then - 1, ThenEndInd[j].End - 1, ifnode, Side.Right);

                // In case the current IF wer'e in, ends before the end of the rule, it means there's either OR or AND after it [example: (IF...THEN...END) OR ~some other rule~]
                if (ThenEndInd[j].End < end)  //(if...end_if..) and ( ...) או (if...end_if..) or ( ...)
                {
                    // decide wether the ifnode created above is part of an OR, or an AND, and create a node according to it.
                    if (Formula[ThenEndInd[j].End + 1] == Com.OR)
                    {
                        node = new TreeNode("OR", null, null);
                    }
                    else if (Formula[ThenEndInd[j].End + 1] == Com.AND)
                    {
                        node = new TreeNode("AND", null, null);
                    }

                    // To prevent repeating code you can set the node's children outside the ifs above.
                    node.SetChild(Side.Left, ifnode);
                    BuildTree(Formula, ThenEndInd[j].End + 2, end, node, Side.Right);
                }
                // if there's no continuation after the IF then just assign it as the node to set.
                else
                {
                    node = ifnode;
                }
            }
            // in case this is a compare rule
            else
            {
                // Create a leaf node in the tree which it's value is the compare formula.
                node = new TreeNode(Formula[j], null, null);
            }

            // If the given "parent" is null it means that "node" should be the root, otherwise, put node in the assigned place.
            if (parent == null)
            {
                parent = node;
            }
            else
            {
                parent.SetChild(side, node);
            }

        }

        /// <summary>
        /// הפונקציה עוברת על המערך של הפורמולה דוחפת למחסנית את האינדקס של ה IF
        /// או NOT
        /// בהתאמה.
        /// ברגע שמגלים THEN
        /// מציצים במחסנית האיפ ושמים במערך במיקום שנמצא כרגע במחסנית (טופ) נקודה THEN
        /// ThenEndInd[IfIndexStack.top()].Then = i;
        /// ברגע שמגיעים ל ENDIF
        /// ThenEndInd[IfIndexStack.pop()].Then = i;
        /// 
        /// אותו דבר ל NOT
        /// רק בלי השורה של הטופ.
        /// </summary>
        /// <param name="Formula"></param>
        private static void Analyze(string[] Formula)
        {
            Stack<int> IfIndexStack = new Stack<int>();
            Stack<int> NotIndexStack = new Stack<int>();
            ThenEndInd = new Limitter[Formula.Length - 1];

            for (int i = 0; i < Formula.Length - 1; i++)
            {
                if (Formula[i].Equals("ENDIF"))
                {
                    ThenEndInd[IfIndexStack.Pop()].End = i;
                }

                else if (Formula[i].Equals("ENDNOT"))
                {
                    ThenEndInd[NotIndexStack.Pop()].End = i;
                }
                else if (Formula[i].Equals(Com.IF))
                {
                    IfIndexStack.Push(i);
                }

                else if (Formula[i].Equals(Com.NOT))
                {
                    NotIndexStack.Push(i);
                }

                else if (Formula[i].Equals(Com.THEN))
                {
                    ThenEndInd[IfIndexStack.Peek()].Then = i;
                }


            }
        }
    }
}
